﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity.DataModel;

namespace T.FAS.Runtime.DataModel.Dao
{
    /// <summary>
    /// 校验委托类
    /// </summary>
    /// <param name="ValidateObj">待校验对象的引用</param>
    /// <param name="paramList">其余参数列表</param>
    /// <returns></returns>
    public delegate AssertReulst Validate(ref object ValidateObj, params object[] paramList);

    /// <summary>
    /// 完成校验委托
    /// </summary>
    /// <param name="ValidateObj"></param>
    public delegate void FinishValidate(ref object ValidateObj);

    //public delegate bool ErrorHandler(List<AssertErrorFrame> errorFrames);

    //断言类
    public class FASAssert
    {
        #region Private Fields & Properties
        private bool isCriticalError = false;
        private StringBuilder ErrorMsgBuilder = new StringBuilder();
        private AssertReulst result = new AssertReulst
        {
            Result = true,
            ErrorMsg = string.Empty,
            ValidateErrorFrams = new List<AssertErrorFrame>(),
        };

        /// <summary>
        /// 错误处理器集合
        /// </summary>
        private Dictionary<string, Func<AssertErrorFrame, ErrorRepairResult>>
            ErrorHandler = new Dictionary<string, Func<AssertErrorFrame, ErrorRepairResult>>();

        /// <summary>
        /// 待校验对象引用
        /// </summary>
        private object ValidateObject;
        #endregion

        /// <summary>
        /// 校验结果
        /// </summary>
        public AssertReulst Result
        {
            get
            {
                return result;
            }
        }

        /// <summary>
        /// 校验器注册事件
        /// </summary>
        public event Validate Validator;
        
        /// <summary>
        /// 完成校验事件
        /// </summary>
        public event FinishValidate FinishValidate;

        #region 断言方法
        /// <summary>
        /// 判断condition是否为True，否则记录错误信息
        /// </summary>
        /// <param name="condition">条件</param>
        /// <param name="errorMsg">报错信息</param>
        /// <param name="isThrowException">是否直接抛出异常</param>
        public void IsTrue(bool condition, string errorMsg = null, bool isThrowException = false, AssertErrorFrame frame = null)
        {
            bool assertFlag = condition == true;
            if (!assertFlag)
            {
                RecordError(errorMsg);
                if (frame != null)
                    result.ValidateErrorFrams.Add(frame);
            }
            if (isThrowException)
                FailIfErrorResult();
        }

        /// <summary>
        /// 判断condition是否为False，否则记录错误信息
        /// </summary>
        /// <param name="condition">条件</param>
        /// <param name="errorMsg">报错信息</param>
        /// <param name="isThrowException">是否直接抛出异常</param>
        public void IsFalse(bool condition, string errorMsg = null, bool isThrowException = false, AssertErrorFrame frame = null)
        {
            bool assertFlag = condition == false;
            if (!assertFlag)
            {
                RecordError(errorMsg);
                if (frame != null)
                    result.ValidateErrorFrams.Add(frame);
            }
            if (isThrowException)
                FailIfErrorResult();
        }

        /// <summary>
        /// 使用!=判断两个对象是否相等
        /// </summary>
        /// <param name="obj1"></param>
        /// <param name="obj2"></param>
        /// <param name="errorMsg"></param>
        /// <param name="isThrowException"></param>
        public void AreEqual(object obj1, object obj2, string errorMsg = null, bool isThrowException = false, AssertErrorFrame frame = null)
        {
            bool assertFlag = obj1 == obj2;
            if (!assertFlag)
            {
                RecordError(errorMsg);
                if (frame != null)
                    result.ValidateErrorFrams.Add(frame);
            }
            if (isThrowException)
                FailIfErrorResult();
        }

        /// <summary>
        /// 如果是string，使用string.IsNotNullOrEmpty判断, 其余使用object == null判断
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="errorMsg"></param>
        /// <param name="isThrowException">是否直接抛出异常</param>
        public void IsNotNull(object obj, string errorMsg = null, bool isThrowException = false, AssertErrorFrame frame = null)
        {
            bool assertFlag = true;
            if (obj == null)
                assertFlag = false;
            else if (obj is string str)
            {
                if (string.IsNullOrEmpty(str))
                    assertFlag = false;
            }
            if (!assertFlag)
            {
                RecordError(errorMsg);
                if (frame != null)
                    result.ValidateErrorFrams.Add(frame);
            }
            if (isThrowException)
                FailIfErrorResult();
        }

        #endregion

        /// <summary>
        /// 构造断言结果
        /// </summary>
        public AssertReulst GetAssertResult()
        {
            result.ErrorMsg = ErrorMsgBuilder.ToString();
            return result;
        }
         
        /// <summary>
        /// 如果校验失败，主动抛出异常
        /// </summary>
        public void FailIfErrorResult()
        {
            GetAssertResult();
            if (result.Result == false)
            {
                isCriticalError = true;
                Fail(result);
            }
        }

        /// <summary>
        /// 执行校验
        /// </summary>
        /// <param name="validateContext"></param>
        public void Validate(ref object validateObj, int retryCount = 0, params object[] paramList)
        {
            if (validateObj == null)
                throw new Exception($"校验失败，待校验数据为null");
            if (Validator == null)
                throw new Exception($"校验失败，未注册校验器");
            try
            {
                if (retryCount > 20)
                {
                    string extErrorMsg = default;
                    if(Result?.ValidateErrorFrams != null && Result?.ValidateErrorFrams.Count > 0)
                    {
                        var errorList = Result.ValidateErrorFrams.Select(f => f.ValidateErrorCode)?.Distinct();
                        extErrorMsg = string.Join(",", errorList);
                    }
                    throw new FasException(DataModelErrorCode.AssertError, $"{validateObj.GetType().Name}类型校验失败, 尝试执行修复20次后仍未修复异常, 额外信息:{extErrorMsg}");
                }
                this.ValidateObject = validateObj;
                //执行外部校验逻辑
                Validator(ref validateObj, paramList);
                //外部校验逻辑执行完成后，触发完成事件
                FinishValidate(ref validateObj);
            }
            catch (FASReValidateException)
            {
                //捕获到重试异常时，置空校验结果，重新执行校验
                ResetAssertResult();
                Validate(ref validateObj, ++retryCount, paramList);
            }
            catch (Exception e)
            {
                throw new FasException(DataModelErrorCode.AssertError, $"{validateObj.GetType().Name}类型校验失败, msg:{e.Message}", e);
            }
        }

        /// <summary>
        /// 注册错误处理器
        /// </summary>
        /// <param name="errorCode"></param>
        /// <param name="func"></param>
        public void RegistErrorHandler(string errorCode, Func<AssertErrorFrame, ErrorRepairResult> func)
        {
            if (!ErrorHandler.ContainsKey(errorCode))
            {
                ErrorHandler[errorCode] = func;
            }
        }

        /// <summary>
        /// 构造错误帧
        /// </summary>
        /// <param name="errorCode"></param>
        /// <returns></returns>
        public AssertErrorFrame CreateFrame(string errorCode)
        {
            //ValidateObject为空说明没有走Validate校验方法
            return new AssertErrorFrame
            {
                ValidateErrorCode = errorCode,
                ValidateContext = new AssertValidateContext
                {
                    ValidateObj = ValidateObject
                }
            };
        }

        #region Private Func
        /// <summary>
        /// 修复
        /// </summary>
        /// <param name="assertErrorFrame"></param>
        /// <returns></returns>
        private ErrorRepairResult Repair(AssertErrorFrame assertErrorFrame)
        {
            //监测到FASExp后，执行修复逻辑
            if (!ErrorHandler.ContainsKey(assertErrorFrame.ValidateErrorCode))
                return ErrorRepairResult.Error;
            //ValidateObj为空说明不是走的Validate方法，不提供修复功能
            if (assertErrorFrame.ValidateContext.ValidateObj == null)
                return ErrorRepairResult.Error;
            ErrorRepairResult repairResult;
            try
            {
                //执行修复逻辑
                repairResult = ErrorHandler[assertErrorFrame.ValidateErrorCode](assertErrorFrame);
                return repairResult;
            }
            catch
            {
                return ErrorRepairResult.Error;
            }
        }

        /// <summary>
        /// 异常处理
        /// </summary>
        /// <param name="assertReulst"></param>
        private void Fail(AssertReulst assertReulst)
        {
            ErrorRepairResult flag = ErrorRepairResult.Error;
            if (ErrorHandler.Count > 0)
            {
                foreach (var frame in assertReulst.ValidateErrorFrams)
                {
                    var result = Repair(frame);
                    if (result == ErrorRepairResult.Error)
                    {
                        flag = ErrorRepairResult.Error;
                        break;
                    }
                    else if (result == ErrorRepairResult.SuccessAndRetry)
                    {
                        flag = ErrorRepairResult.SuccessAndRetry;
                        break;
                    }
                    else if (result == ErrorRepairResult.Success)
                        flag = ErrorRepairResult.Success;
                }
            }
            //尝试修复失败，抛出原有异常
            if (flag == ErrorRepairResult.Error)
                throw new FasException(DataModelErrorCode.AssertError, assertReulst?.ErrorMsg);
            //应当重试时，抛出重试异常
            else if (flag == ErrorRepairResult.SuccessAndRetry)
                throw new FASReValidateException();
            //否则什么都不做，接续执行验证
            else { } 
        }

        /// <summary>
        /// 记录异常
        /// </summary>
        /// <param name="errorMsg"></param>
        private void RecordError(string errorMsg)
        {
            result.Result = false;
            ErrorMsgBuilder.Append(errorMsg);
            ErrorMsgBuilder.Append("\r\n");
        }

        /// <summary>
        /// 置空校验结果
        /// </summary>
        private void ResetAssertResult()
        {
            this.result = new AssertReulst
            {
                Result = true,
                ErrorMsg = string.Empty,
                ValidateErrorFrams = new List<AssertErrorFrame>(),
            };
        }

        #endregion

        #region 内部类
        public class FASAssertException : Exception
        {
            public string ErrorCode { get; set; }
            public string AssertMsg { get; set; }
            public AssertErrorFrame AssertErrorFrame { get; set; }

            private FASAssertException(string code) : base()
            {
                this.ErrorCode = code;
            }
            public FASAssertException(string code, string msg, AssertErrorFrame assertErrorFrame) : base(msg)
            {
                this.ErrorCode = code;
                this.AssertMsg = msg;
                this.AssertErrorFrame = assertErrorFrame;
            }
            public FASAssertException(string code, string msg, AssertErrorFrame assertErrorFrame, Exception exp) : base(msg, exp)
            {
                this.ErrorCode = code;
                this.AssertMsg = msg;
                this.AssertErrorFrame = assertErrorFrame;
            }
        }

        public class FASReValidateException : Exception
        {

        }
        #endregion
    }

    /// <summary>
    /// 校验结果
    /// </summary>
    public class AssertReulst
    {
        /// <summary>
        /// 校验结果
        /// </summary>
        public bool Result { get; set; }

        /// <summary>
        /// 错误信息
        /// </summary>
        public string ErrorMsg { get; set; }

        /// <summary>
        /// 校验错误帧
        /// </summary>
        public List<AssertErrorFrame> ValidateErrorFrams { get; set; }
    }

    /// <summary>
    /// 错误处理结果
    /// </summary>
    public enum ErrorRepairResult
    {
        Error,
        Success,
        SuccessAndRetry,
    }

    /// <summary>
    /// 断言帧
    /// </summary>
    public class AssertErrorFrame
    {
        /// <summary>
        /// 校验错误码
        /// </summary>
        public string ValidateErrorCode { get; set; }

        /// <summary>
        /// 校验上下文，后续供修复或其他用途
        /// </summary>
        public AssertValidateContext ValidateContext { get; set; }
    }

    /// <summary>
    /// 断言错误帧上下文
    /// </summary>
    public class AssertValidateContext
    {
        /// <summary>
        /// 待校验对象引用
        /// </summary>
        public object ValidateObj { get; set; }

        /// <summary>
        /// 其余的参数按需传递
        /// </summary>
        public dynamic OtherParams { get; set; }
    }
}
